package lol.clann.bukkit.pluginboot.util;

import lol.clann.clannboot.annotation.inject.Inject;
import lol.clann.clannboot.context.BeanContext;
import org.apache.commons.lang.Validate;
import org.bukkit.block.Block;
import org.bukkit.entity.Entity;
import org.bukkit.inventory.ItemStack;

import java.util.ArrayList;
import java.util.List;

/**
 * @author pyz
 * @date 2018/8/7 下午9:04
 */
public class NBTEditor<T> {

    private final T handle;
    private final Object tag;
    private final List<Object> roots = new ArrayList();
    private final List<Object> os = new ArrayList();
    private Object now;

    @Inject
    private ItemStackUtils itemUtils;
    @Inject
    private BlockUtils blockUtils;
    @Inject
    private WorldUtils worldUtils;
    @Inject
    private TileEntityUtils tileEntityUtils;
    @Inject
    private EntityUtils entityUtils;
    @Inject
    private NBTCompoundUtils nbtCompoundUtils;
    @Inject
    private NBTListUtils nbtListUtils;
    @Inject
    private NBTUtils nbtUtils;

    private NBTEditor(T handle) {
        BeanContext.inject(this);
        this.handle = handle;
        if (handle instanceof ItemStack) {
            tag = itemUtils.getTagCompound((ItemStack) handle, true);
        } else if (handle instanceof Block) {
            Block b = (Block) handle;
            Object te = worldUtils.getTileEntity(b.getWorld(), b.getX(), b.getY(), b.getZ());
            if (te == null) {
                tag = nbtCompoundUtils.newTagCompound();
            } else {
                tag = tileEntityUtils.toCompound(te);
            }
        } else if (handle instanceof Entity) {
            tag = entityUtils.toCompound((Entity) handle);
        } else {
            throw new UnsupportedOperationException("未知参数:" + handle.toString());
        }
        os.add(now = tag);
    }

    private NBTEditor(Object nbtTag, Object o) {
        BeanContext.inject(this);
        this.tag = nbtTag;
        os.add(tag);
        now = tag;
        handle = null;
    }

    public static NBTEditor fromTag(Object nbtTag) {
        return new NBTEditor(nbtTag, null);
    }

    public static <T> NBTEditor<T> fromHandle(T handle) {
        return new NBTEditor<>(handle);
    }

    public Object getNow() {
        return now;
    }

    public Object getTag() {
        return tag;
    }

    public T getHandle() {
        return handle;
    }

    public boolean canSave() {
        return handle != null;
    }

    /**
     * 只有当handle != null时才允许调用
     */
    public void save() {
        Validate.isTrue(canSave(), "当前没有处于直接编辑物品/实体/方块的模式，无法直接保存");
        if (handle instanceof ItemStack) {
            itemUtils.fill(handle, tag);
        } else if (handle instanceof Block) {
            tileEntityUtils.fill(worldUtils.getTileEntity((Block) handle), tag);
        } else if (handle instanceof Entity) {
            entityUtils.fill(handle, tag);
        } else {
            throw new UnsupportedOperationException("未知参数:" + handle.toString());
        }
    }

    public NBTEditor reset() {
        this.roots.clear();
        this.os.clear();
        now = tag;
        os.add(tag);
        return this;
    }


    public NBTEditor remove(String[] roots) throws IllegalArgumentException {
        reset();
        for (int i = 0; i < roots.length - 1; i++) {
            next(roots[i]);
        }
        remove(roots[roots.length - 1]);
        return this;
    }

    public NBTEditor set(String[] roots, String value) {
        reset();
        for (int i = 0; i < roots.length - 1; i++) {
            next(roots[i]);
        }
        set(roots[roots.length - 1], value);
        return this;
    }

    public NBTEditor remove(String key) throws IllegalArgumentException {
        Validate.isTrue(now != null && nbtUtils.isTagCompound(now), "要移除当前分支的子分支，请使用指令/nbt remove key");
        nbtCompoundUtils.removeTag(now, key);
        return this;
    }

    public NBTEditor remove(int index) throws IllegalArgumentException {
        Validate.isTrue(now != null && nbtUtils.isTagList(now), "要移除当前分支的子分支，请使用指令/nbt remove index");
        nbtListUtils.removeTag(now, index);
        return this;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Object o : roots) {
            sb.append("§c").append(o).append("§f|");
        }
        sb.append("\n§f");

        sb.append(nbtUtils.toJson(now, true));
        return sb.toString();
    }

    private void next(Object key, Object nbtBase) {
        roots.add(key);
        os.add(nbtBase);
        now = nbtBase;
    }

    public NBTEditor next(String key) {
        if (now == null) {
            Object o = nbtCompoundUtils.getTag(tag, key);
            Validate.notNull(o, "不存在此键:" + key);
            Validate.isTrue(nbtUtils.isTagList(o) || nbtUtils.isTagCompound(o), "该key对应的是基础值，请直接修改/nbt  set key value或删除/nbt remove key");
            next(key, o);
        } else if (nbtUtils.isTagCompound(now)) {
            Object o = nbtCompoundUtils.getTag(now, key);
            Validate.notNull(o, "不存在此键:" + key);
            Validate.isTrue(nbtUtils.isTagList(o) || nbtUtils.isTagCompound(o), "该key对应的是基础值，请直接修改/nbt  set key value或删除/nbt remove key");
            next(key, o);
        } else {
            int index;
            try {
                index = Integer.valueOf(key);
            } catch (Exception e) {
                throw new NumberFormatException("当前分支应使用指令/nbt get index");
            }
            Validate.isTrue(nbtUtils.isTagList(now), "非法操作");
            Object o = nbtListUtils.getCompoundTagAt(now, index);
            Validate.isTrue(nbtUtils.isTagList(o) || nbtUtils.isTagCompound(o), "该index对应的是基础值，请直接修改/nbt  set index value或删除/nbt remove index");
            next(index, o);
        }
        return this;
    }

    public NBTEditor pre() {
        Validate.isTrue(roots.size() >= 1 && os.size() >= 2, "当前处于顶层分支，无法执行该操作");
        roots.remove(roots.size() - 1);
        os.remove(os.size() - 1);
        now = os.get(os.size() - 1);
        return this;
    }

    public NBTEditor set(String key, String value) {
        Object o = wrap(value);
        if (now == null) {
            nbtCompoundUtils.setTag(tag, key, o);
        } else if (nbtUtils.isTagCompound(now)) {
            nbtCompoundUtils.setTag(now, key, o);
        } else {
            Validate.isTrue(nbtUtils.isTagList(now), "非法操作");
            int index = -1;
            try {
                index = Integer.valueOf(key);
            } catch (Exception e) {
                throw new UnknownError("index必须为非负整数:" + key);
            }
            add(now, index, o);
        }
        return this;
    }

    public NBTEditor set(int index, String value) {
        Object o = wrap(value);
        Validate.isTrue(now != null && nbtUtils.isTagList(now), "当前分支应使用指令/nbt set key value");
        add(now, index, o);
        return this;
    }

    private void add(Object nbtList, int index, Object nbtBase) {
        if (index >= nbtListUtils.getSize(nbtList)) {
            nbtListUtils.appendTag(nbtList, nbtBase);
        } else {
            nbtListUtils.setTag(nbtList, index, nbtBase);
        }
    }

    private Object wrap(String value) {
        switch (value) {
            case "{}":
                return nbtCompoundUtils.newTagCompound();
            case "[]":
                return nbtListUtils.newList();
            default:
                Object base = nbtUtils.wrap(value.replace("&", "§"));
                return base;
        }
    }
}